package org.ellipticcurve;

import org.apache.log4j.Logger;
import org.bouncycastle.util.encoders.Hex;
import org.bouncycastle.util.test.FixedSecureRandom;
import org.bouncycastle.math.ec.ECCurve;
import org.bouncycastle.math.ec.ECPoint;
import org.bouncycastle.math.ec.ECFieldElement;
import org.bouncycastle.crypto.params.*;
import org.bouncycastle.crypto.signers.ECGOST3410Signer;
import org.bouncycastle.crypto.generators.ECKeyPairGenerator;
import org.bouncycastle.crypto.AsymmetricCipherKeyPair;
import org.bouncycastle.crypto.digests.GOST3411Digest;

import java.math.BigInteger;
import java.security.SecureRandom;

/**
 * ECGOST3410 tests are taken from GOST R 34.10-2001.
 */
public class ECGOST3410 {
	static final Logger LOG = Logger.getLogger(ECGOST3410.class);

//	byte[] hashmessage = Hex
//			.decode("3042453136414534424341374533364339313734453431443642453241453435");

	/**
	 * ECGOST3410 over the field Fp<br>
	 */
	BigInteger r = new BigInteger(
			"29700980915817952874371204983938256990422752107994319651632687982059210933395");
	BigInteger s = new BigInteger(
			"574973400270084654178925310019147038455227042649098563933718999175515839552");

	byte[] kData = new BigInteger(
			"53854137677348463731403841147996619241504003434302020712960838528893196233395")
			.toByteArray();

	SecureRandom k = new FixedSecureRandom(kData);

	/**
	 * Test Sign & Verify with C parameters see:
	 * http://www.ietf.org/internet-drafts/draft-popov-cryptopro-cpalgs-01.txt
	 * gostR3410-2001-CryptoPro-C-ParamSet P.48
	 */
	private void ecGOST3410_CParam() {

		printData();

		SecureRandom random = new SecureRandom();

		BigInteger mod_p = new BigInteger(
				"70390085352083305199547718019018437841079516630045180471284346843705633502619"); // p
		LOG.info("простое число p - модуль эллиптической кривой");
		LOG.info("mod_p=" + mod_p);

		BigInteger a = new BigInteger(
				"70390085352083305199547718019018437841079516630045180471284346843705633502616");

		BigInteger b = new BigInteger("32858");

		ECCurve.Fp curve = new ECCurve.Fp(mod_p, a, b);

		LOG.info("Эллиптическая кривая");
		LOG.info("q = порядок циклической подгруппы группы точек эллиптической");
		LOG.info("ECCurve curve = " + curve.toString());

		LOG.info("Коэффициенты эллиптической кривой");
		LOG.info("a=" + a);
		LOG.info("b=" + b);

		BigInteger x = new BigInteger("0");

		BigInteger y = new BigInteger(
				"29818893917731240733471273240314769927240550812383695689146495261604565990247");

		BigInteger q = new BigInteger(
				"70390085352083305199547718019018437840920882647164081035322601458352298396601");

		LOG.info("x=" + x);
		LOG.info("y=" + y);
		LOG.info("q=" + q);

		ECFieldElement xFieldElement = new ECFieldElement.Fp(mod_p, x);

		ECFieldElement yFieldElement = new ECFieldElement.Fp(mod_p, y);

		LOG.info("ECFieldElement x =" + xFieldElement);
		LOG.info("ECFieldElement y = " + yFieldElement);

		ECPoint G = new ECPoint.Fp(curve, xFieldElement, yFieldElement);

		LOG.info("ECPoint G = " + G.toString());

		ECDomainParameters params = new ECDomainParameters(curve, G, q);

		LOG.info("ECDomainParameters params = " + params.toString());
		LOG.info("SecureRandom random = " + random.toString());

		ECKeyPairGenerator pGen = new ECKeyPairGenerator();

		ECKeyGenerationParameters genParam = new ECKeyGenerationParameters(
				params, random);

		pGen.init(genParam);

		LOG.info("ECKeyPairGenerator pGen = " + pGen.toString());

		AsymmetricCipherKeyPair pair = pGen.generateKeyPair();

		LOG.info("AsymmetricCipherKeyPair pair = " + pair.toString());

		ParametersWithRandom param = new ParametersWithRandom(
				pair.getPrivate(), random);

		LOG.info("ParametersWithRandom param = " + param.toString());

		ECGOST3410Signer ecgost3410 = new ECGOST3410Signer();

		ecgost3410.init(true, param);

		LOG.info("ECGOST3410Signer ecgost3410 = " + ecgost3410.toString());

		// get hash message using the digest GOST3411.
		
		String text = "Message for sign";
		
		LOG.info("Text=" + text);
		
		byte[] message = text.getBytes();
		GOST3411Digest gost3411 = new GOST3411Digest();
		gost3411.update(message, 0, message.length);

		LOG.info("GOST3411Digest gost3411 = " + gost3411.toString());

		byte[] hashmessage = new byte[gost3411.getDigestSize()];
		gost3411.doFinal(hashmessage, 0);

		BigInteger[] sig = ecgost3410.generateSignature(hashmessage);
		
		LOG.info("sig[0]=" + sig[0]);
		
		LOG.info("sig[1]=" + sig[1]);

		ecgost3410.init(false, pair.getPublic());

		if (!ecgost3410.verifySignature(hashmessage, sig[0], sig[1])) {
			LOG.warn("signature fails");
		}
	}

	public void printData() {
		LOG.info("r=" + r);
		LOG.info("s=" + s);
		LOG.info("kData=" + byteArrayToString(kData));
		LOG.info("k=" + k.toString());
	}

	public String byteArrayToString(byte[] source) {
		return new String(new BigInteger(source).toString(2));
	}

	public static void main(String[] args) {
		ECGOST3410 ec = new ECGOST3410();
		ec.ecGOST3410_CParam();
		LOG.info("Good");
	}
}
